local mdb_service = require 'mc.mdb.mdb_service'
local log = require 'mc.logging'

local m = {}

local mdb_setting = {
    IsValidId = false,
    GroupId = 0,
    LinkStatus = "UnConnected",
    MgmtPortNumber = 255,
    MgmtType = cjson.null,
    NegotiatedSpeedMbps = 0,
    EthernetInterfaces = {
        Path = "",
        Interface = ""
    },
    Ipv4 = {
        Path = "",
        Interface = ""
    },
    Ipv6 = {
        Path = "",
        Interface = "",
        IpMode = "IpMode",
        IpAddr = "IpAddr",
        DefaultGateway = "DefaultGateway",
        SetIpAddr = "SetIpAddr",
        SetDefaultGateway = "SetDefaultGateway"
    },
    EthernetInterfacesPortId = {
        Path = "",
        Interface = "",
        PortNumber = 255
    },
    EthernetInterfacesDNS = {
        Path = "",
        Interface = ""
    }
}

local function get_netinterface_object(mdb, bus, mdb_path, interface_name)
    local ok, netinterface_object = pcall(function()
        return mdb.get_object(bus, mdb_path, interface_name)
    end)
    if not ok then
        log:debug('Incorrect mdb path or interface, path = %s, interface = %s', mdb_path, interface_name)
        return nil
    end

    return netinterface_object
end

-- 获取和当前mac地址匹配的网口对象，若没有匹配上就返回nil
local function get_mac_patched_eth_object(mdb, bus, mdb_path, interface_name, mac_id)
    local netinterface_object = get_netinterface_object(mdb, bus, mdb_path, interface_name)

    if netinterface_object ~= nil then
        local net_mac = netinterface_object.Mac
        if net_mac == mac_id then
            return netinterface_object
        end
    end

    return nil
end

-- 获取最外围的LinkStatus
local function set_link_state(mdb, bus, mdb_path, port_id)
    mdb_setting.LinkStatus = "UnConnected"
    mdb_setting.MgmtPortNumber = 255
    mdb_setting.MgmtType = cjson.null
    mdb_setting.NegotiatedSpeedMbps = 0
    if port_id == 255 then
        return
    end
    local link_obj = get_netinterface_object(mdb, bus,
        mdb_path .. "/" .. port_id, "bmc.kepler.Managers.EthernetInterfaces.MgmtPort")
    if link_obj ~= nil and link_obj.LinkStatus == "Connected" then
        mdb_setting.LinkStatus = link_obj.LinkStatus
        mdb_setting.MgmtType = link_obj.Type
        mdb_setting.MgmtPortNumber = link_obj.DevicePortId
        mdb_setting.NegotiatedSpeedMbps = link_obj.NegotiatedSpeedMbps
    end
    return
end

-- 获取和当前mac地址匹配的组对象，若没有匹配上就返回nil，由于兼容性问题，旧接口没有OutType因此二者处理不同
local function get_mac_patched_ethgroup_object(mdb, bus, mdb_path, interface_name, mac_id)
    local netinterface_object = get_netinterface_object(mdb, bus, mdb_path, interface_name)

    if netinterface_object ~= nil and netinterface_object.OutType == 2 then
        local net_mac = netinterface_object.Mac
        if net_mac == mac_id then
            return netinterface_object
        end
    end

    return nil
end

-- 获取和当前mac地址匹配的网口组对象，若没有匹配上就返回nil
local function patch_mac_to_ethgroup(mdb, bus, mdb_path, interface_name, mac_id)
    local ok, rsp = pcall(mdb_service.get_sub_paths, bus, mdb_path .. "/EthGroup", 1, {interface_name})

    if not ok then
        log:debug('Incorrect parent path or interface, path = %s, interface = %s', mdb_path, interface_name)
        return false
    end

    for _, sub_path in pairs(rsp.SubPaths) do
        local ethgroup_object = get_mac_patched_ethgroup_object(mdb, bus, sub_path, interface_name, mac_id)
        if ethgroup_object ~= nil then
            mdb_setting.IsValidId = true
            mdb_setting.GroupId = ethgroup_object.GroupId
            mdb_setting.EthernetInterfaces.Path = sub_path
            mdb_setting.EthernetInterfaces.Interface = "bmc.kepler.Managers.EthernetInterfaces.EthGroup"
            mdb_setting.EthernetInterfaces.VLANEnable = "VLANEnabled"
            mdb_setting.EthernetInterfacesPortId.Path = mdb_path .. "/" .. ethgroup_object.ActivePortId
            mdb_setting.EthernetInterfacesPortId.Interface = "bmc.kepler.Managers.EthernetInterfaces.MgmtPort"
            mdb_setting.EthernetInterfacesPortId.PortNumber = ethgroup_object.ActivePortId
            set_link_state(mdb, bus, mdb_path, mdb_setting.EthernetInterfacesPortId.PortNumber)
            mdb_setting.Ipv4.Path = sub_path
            mdb_setting.Ipv4.Interface = "bmc.kepler.Managers.EthernetInterfaces.EthGroup"
            mdb_setting.Ipv6.Path = sub_path
            mdb_setting.Ipv6.Interface = "bmc.kepler.Managers.EthernetInterfaces.EthGroup"
            mdb_setting.Ipv6.IpMode = "Ipv6Mode"
            mdb_setting.Ipv6.IpAddr = "Ipv6Addr"
            mdb_setting.Ipv6.DefaultGateway = "Ipv6DefaultGateway"
            mdb_setting.Ipv6.SetIpAddr = "SetIpv6Addr"
            mdb_setting.Ipv6.SetDefaultGateway = "SetIpv6DefaultGateway"
            mdb_setting.EthernetInterfacesDNS.Path = mdb_path .. "/DNS"
            mdb_setting.EthernetInterfacesDNS.Interface = "bmc.kepler.Managers.EthernetInterfaces.DNS"
            return true
        end
    end

    return false
end

local function patch_mac_to_eth(mdb, bus, mdb_path, interface_name, mac_id)
    local netinterface_object = get_mac_patched_eth_object(mdb, bus, mdb_path, interface_name, mac_id)
    if netinterface_object ~= nil then
        mdb_setting.IsValidId = true
        mdb_setting.GroupId = 0
        mdb_setting.EthernetInterfaces.Path = mdb_path
        mdb_setting.EthernetInterfaces.Interface = "bmc.kepler.Managers.EthernetInterfaces"
        mdb_setting.EthernetInterfaces.VLANEnable = "VLANEnable"
        mdb_setting.EthernetInterfacesPortId.Path = mdb_path .. "/" .. tostring(netinterface_object.PortId)
        mdb_setting.EthernetInterfacesPortId.Interface = "bmc.kepler.Managers.EthernetInterfaces.MgmtPort"
        mdb_setting.EthernetInterfacesPortId.PortNumber = netinterface_object.PortId
        set_link_state(mdb, bus, mdb_path, mdb_setting.EthernetInterfacesPortId.PortNumber)
        mdb_setting.Ipv4.Path = mdb_path .. "/Ipv4"
        mdb_setting.Ipv4.Interface = "bmc.kepler.Managers.EthernetInterfaces.Ipv4"
        mdb_setting.Ipv6.Path = mdb_path .. "/Ipv6"
        mdb_setting.Ipv6.Interface = "bmc.kepler.Managers.EthernetInterfaces.Ipv6"
        mdb_setting.Ipv6.IpMode = "IpMode"
        mdb_setting.Ipv6.IpAddr = "IpAddr"
        mdb_setting.Ipv6.DefaultGateway = "DefaultGateway"
        mdb_setting.Ipv6.SetIpAddr = "SetIpAddr"
        mdb_setting.Ipv6.SetDefaultGateway = "SetDefaultGateway"
        mdb_setting.EthernetInterfacesDNS.Path = mdb_path .. "/DNS"
        mdb_setting.EthernetInterfacesDNS.Interface = "bmc.kepler.Managers.EthernetInterfaces.DNS"
        return true
    end

    return false
end

function m.patch_eth_info(manager_id, eth_id)
    -- 格式化eth_id为Mac地址
    if #eth_id ~= 12 then
        return false
    end
    local mac_id = string.format('%s:%s:%s:%s:%s:%s', string.sub(eth_id, 1, 2), string.sub(eth_id, 3, 4),
    string.sub(eth_id, 5, 6), string.sub(eth_id, 7,8), string.sub(eth_id, 9, 10), string.sub(eth_id, 11, 12))
    
    local m_path = "/bmc/kepler/Managers/" .. manager_id .. "/EthernetInterfaces"

    log:debug("[PATCH MAC] mdb_path = %s, mac = %s", m_path, mac_id)

    -- 检查是否符合组信息挂载在EthernetInterfaces下的情况
    if patch_mac_to_eth(mdb, bus, m_path, "bmc.kepler.Managers.EthernetInterfaces", mac_id) then
        return mdb_setting
    end

    -- 检查是否符合组信息挂载在EthGroup子目录下的情况
    if patch_mac_to_ethgroup(mdb, bus, m_path, "bmc.kepler.Managers.EthernetInterfaces.EthGroup", mac_id) then
        return mdb_setting
    end

    -- 必须要专门赋值，实测前端可能会使用旧数据，使用默认值可能会意外访问到非法mac
    mdb_setting.IsValidId = false
    return mdb_setting
end

function m.get_adaptive_port(port_table, group_id)
    local adaptive_port = {}
    pcall(function()
        for _, path in pairs(port_table) do
            local port = {}
            local port_obj = mdb.get_object(bus, path, "bmc.kepler.Managers.EthernetInterfaces.MgmtPort")
            if port_obj.CurrentGroupId == nil or port_obj.CurrentGroupId == group_id then
                port["Type"] = port_obj.Type
                port["PortNumber"] = port_obj.DevicePortId
                port["AdaptiveFlag"] = port_obj.AdaptiveFlag
                table.insert(adaptive_port, port)
            end
        end
    end)
    return adaptive_port
end

function m.get_odata(manager_id)
    local odatas = {
        Count = 0,
        Odatas = {}
    }
    local m_path = "/bmc/kepler/Managers/" .. manager_id .. "/EthernetInterfaces"
    log:debug("[Get Manage Mac]")
    local ethinterface_obj = get_netinterface_object(mdb, bus, m_path, "bmc.kepler.Managers.EthernetInterfaces")
    if ethinterface_obj ~= nil then
        odatas.Count = odatas.Count + 1
        local mac_str = string.gsub(ethinterface_obj.Mac, ':', '')
        local odata = {}
        odata["@odata.id"] = "/redfish/v1/Managers/1/EthernetInterfaces/" .. mac_str
        table.insert(odatas.Odatas, odata)
    end

    local ok, rsp = pcall(mdb_service.get_sub_paths, bus, m_path .. "/EthGroup",
        1, {"bmc.kepler.Managers.EthernetInterfaces.EthGroup"})

    if not ok then
        log:debug('Incorrect parent path or interface, path = %s, interface = %s',
            m_path, "bmc.kepler.Managers.EthernetInterfaces.EthGroup")
        return odatas
    end

    -- 检查group子目录下的OutType为2的组
    for _, sub_path in pairs(rsp.SubPaths) do
        local ethgroup_obj = get_netinterface_object(mdb, bus, sub_path,
            "bmc.kepler.Managers.EthernetInterfaces.EthGroup")
        if ethgroup_obj ~= nil and ethgroup_obj.OutType == 2 then
            odatas.Count = odatas.Count + 1
            local mac_str = string.gsub(ethgroup_obj.Mac, ':', '')
            local odata = {}
            odata["@odata.id"] = "/redfish/v1/Managers/1/EthernetInterfaces/" .. mac_str
            table.insert(odatas.Odatas, odata)
        end
    end

    return odatas
end

return m